package proxyc

import (
	"gitee.com/yysf-xin/dproxy/pkg/app"
	"gitee.com/yysf-xin/dproxy/pkg/db"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"gitee.com/yysf-xin/dproxy/pkg/rest"
	"gitee.com/yysf-xin/dproxy/pkg/svr"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"github.com/go-chi/chi/v5"
	"golang.org/x/net/proxy"
	"net/http"
	"sync"
)

type ProxyControl struct {
	DB        *db.Client
	services  *app.Services
	dialer    proxy.Dialer
	DialerMap map[uint]*svr.ProxyDialer
	pMapLock  sync.RWMutex
}

func (p *ProxyControl) Start() (err error) {
	var proxyList db.ProxyList
	proxyList.ListByEnabled(p.DB.Db)

	for i := range proxyList {
		err := p.addProxy(&proxyList[i])
		if err != nil {
			tools.Log.Println(err)
		}
	}

	return
}

func (p *ProxyControl) Stop() {
}

func (p *ProxyControl) InitRoute(route chi.Router) {
	route.Delete("/{id}", app.JsonHttp(nil, p.delete))
	route.Route("/{id}/", func(r chi.Router) {
		r.Post("/enable", app.JsonHttp(func() interface{} {
			return &model.Enable{}
		}, p.handleEnable))
	})
}

func (p *ProxyControl) delete(_ http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	var m db.Proxy
	err = rest.FindModelByReq(r, &m, p.DB.Db)
	if err != nil {
		return
	}
	err = p.DB.Db.Delete(&m, m.Id).Error
	p.removeProxy(&m)
	return
}

func (p *ProxyControl) handleEnable(_ http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	var m db.Proxy
	err = rest.FindModelByReq(r, &m, p.DB.Db)
	if err != nil {
		return
	}
	p.pMapLock.Lock()
	defer p.pMapLock.Unlock()
	if i.(*model.Enable).Enabled {
		err = p.addProxy(&m)
	} else {
		p.removeProxy(&m)
	}
	return
}

func (p *ProxyControl) removeProxy(d *db.Proxy) {
	di := p.DialerMap[d.Id]
	if di == nil {
		return
	}
	delete(p.DialerMap, d.Id)
	p.services.RemoveService(di)
}

func (p *ProxyControl) addProxy(d *db.Proxy) (err error) {
	p.removeProxy(d)
	dialer, err := svr.NewProxyDialer(d, p.dialer)
	if err == nil {
		p.DialerMap[d.Id] = dialer
		p.services.AddService(dialer)
	}
	return
}

func NewProxyControl(DB *db.Client, services *app.Services, dialer proxy.Dialer) (p *ProxyControl) {
	p = &ProxyControl{
		DB:        DB,
		services:  services,
		dialer:    dialer,
		DialerMap: make(map[uint]*svr.ProxyDialer),
	}
	err := p.Start()

	if err != nil {
		tools.Log.Println(err)
	}

	return
}
